#include "stdafx.h"

#include "source.h"
#include "file.h"
#include <lean/io/filesystem.h>
#include <lean/logging/errors.h>
#include <iomanip>
#include <fstream>

// TODO: Temp
#include "lexer.h"
#include "parser.h"
#include <lean/time/highres_timer.h>

namespace funpl
{

namespace shell
{

// Runs the compiler with the given command line arguments.
int run_compiler(int argc, const char* argv[])
{
	std::vector<const char*> inputs;
	std::string obj;

	for (int i = 0; i < argc; ++i)
	{
		const char *arg = argv[i];

		if (_strnicmp(arg, "/If:", lean::ntarraylen("/If:")) == 0)
			inputs.push_back(&arg[lean::ntarraylen("/If:")]);
		else if (_strnicmp(arg, "/Of:", lean::ntarraylen("/Of:")) == 0)
			obj.assign(&arg[lean::ntarraylen("/Of:")]);
		else
			std::cout << "Unrecognized argument, consult 'help compile' for help: " << arg << std::endl;
	}

	if (inputs.empty())
	{
		LEAN_LOG_ERROR_MSG("No input files specified, consult 'help compile' for help.");
		return -1;
	}

	try
	{
		std::vector<parser::source_file> files;

		for (auto it = inputs.begin(); it != inputs.end(); ++it)
			files.push_back(
					parser::source_file(lean::get_filename(*it), *it, util::file_contents(*it))
				);

		std::ofstream outputFile(obj, std::ofstream::out | std::ofstream::trunc);
		std::ostream &outputStream = (outputFile.is_open()) ? outputFile : std::cout;

		size_t errorCount = 0, warningCount = 0;

		// TODO: temp
		for (auto it = files.begin(); it != files.end(); ++it)
		{
			const char *nextChar = it->text.c_str();

			parser::token token;

			lean::highres_timer lexerTimer;

			while (*nextChar)
			{
				token = parser::next_token(nextChar, false);

/*				outputStream
					<< "Token: " << std::setw(24) << parser::token_type_name(token.type) << std::setw(0)
					<< "; Range: " << lean::from_range<std::string>(token.range) << std::endl;
*/			}

			double lexerTook = lexerTimer.milliseconds();
			std::cout << "Lexing took " << lexerTook << " ms" << std::endl;
			
			ast::tree tree;
			debug::error_info errors;

			lean::highres_timer parserTimer;

			parser::construct_ast(tree, it->text.c_str(), &errors);
			
			double parserTook = parserTimer.milliseconds();

			std::cout << "Parsing took " << parserTook << " ms" << std::endl;

			errorCount += errors.errors.size();
			warningCount += errors.warnings.size();

			for (auto er = errors.errors.begin(); er != errors.errors.end(); ++er)
				std::cout << it->name << '(' << er->line << ", " << er->pos << "): error " << er->code << ": " << er->msg << std::endl;
			for (auto er = errors.warnings.begin(); er != errors.warnings.end(); ++er)
				std::cout << it->name << '(' << er->line << ", " << er->pos << "): warning " << er->code << ": " << er->msg << std::endl;
}

		std::cout << "========== " << errorCount << " errors, " << warningCount << " warnings ==========" << std::endl;
	}
	catch(const std::exception &e)
	{
		LEAN_LOG_ERROR_XMSG(e.what(), "Exception");
		return -1;
	}
	catch(...)
	{
		LEAN_LOG_ERROR_MSG("Unknown exception");
		return -1;
	}

	return 0;
}

// Prints the compiler help.
void print_compiler_help(std::ostream &stream)
{
	stream << " Syntax: funpl compile [/If:<input>] [/Of:<output>]"  << std::endl << std::endl;

	stream << " Arguments:"  << std::endl;
	stream << "  /If            Input file"  << std::endl;
	stream << "  /Of            Obj file"  << std::endl;
}

} // namespace

} // namespace